﻿using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using SharpCmd.IO;

namespace SharpCmd.Windows.Forms
{
	/// <summary>
	/// Column sorter used in listboxes
	/// </summary>
	internal class ColumnSorter : IComparer
	{
		private CaseInsensitiveComparer comparer;

		public ColumnSorter ()
		{
			ColumnToSort = 0;
			SortOrder = SortOrder.Ascending;
			comparer = new CaseInsensitiveComparer ();
		}

		public int ColumnToSort { get; set; }

		public SortOrder SortOrder { get; set; }

		/// <summary>
		/// Compares two objects
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <returns></returns>
		public int Compare (object x, object y)
		{
			//we know arguments are listViewItemsEx
			/* in sorting, directory "always wins"
             * that means it is always sorted first
             * Directories are also always sorted by name ascending
             * Size and date are by default descending
             */
			const int ITEMX = -1;
			const int ITEMY = 1;
			ListViewItemEx itemX = (ListViewItemEx)x;
			ListViewItemEx itemY = (ListViewItemEx)y;
			if (itemX.Info.AlwaysOnTop)
				return ITEMX;
			else if (itemY.Info.AlwaysOnTop)
				return ITEMY;
			else if (itemX.Info.Type == FSObjectType.Directory && itemY.Info.Type == FSObjectType.File)
				return ITEMX; //itemX first
			else if (itemX.Info.Type == FSObjectType.File && itemY.Info.Type == FSObjectType.Directory)
				return ITEMY; //itemY first
			else if (itemX.Info.Type == FSObjectType.Directory && itemY.Info.Type == FSObjectType.Directory)
				return StrCmpLogicalW (itemX.Info.Name, itemY.Info.Name);
			else { //file,file
				int result;
				switch (ColumnToSort) {
				case 0: //name
				case 1: //ext
					result = StrCmpLogicalW (itemX.SubItems [ColumnToSort].Text, itemY.SubItems [ColumnToSort].Text);
					break;
				case 2: //size
					result = (itemX.Info.Size > itemY.Info.Size) ? ITEMX : ITEMY;
					break;
				case 3: //date
				default:
					result = DateTime.Compare (itemX.Info.LastWriteTime, itemY.Info.LastWriteTime);
					break;
				}
				switch (SortOrder) {
				case SortOrder.Ascending:
					return result;
				case SortOrder.Descending:
					return -result; //negative result, to be descending
				default:
					return 0; //no sorting order
				}
			}
		}
		
#if OSX
		private static int StrCmpLogicalW(string psz1, string psz2){
				return NaturalComparer.Compare(psz1,psz2);
		}
#else
        //private static int StrCmpLogicalW(string psz1, string psz2)
        //{
        //    return NaturalComparer.Compare(psz1, psz2);
        //}
        [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
        private static extern int StrCmpLogicalW(string psz1, string psz2);
#endif	
		public static class NaturalComparer
		{
			public static int Compare (string x, string y)
			{
				if (x == null && y == null)
					return 0;
				if (x == null)
					return -1;
				if (y == null)
					return 1;

				int lx = x.Length, ly = y.Length;

				for (int mx = 0, my = 0; mx < lx && my < ly; mx++, my++) {
					if (char.IsDigit (x [mx]) && char.IsDigit (y [my])) {
						long vx = 0, vy = 0;

						for (; mx < lx && char.IsDigit(x[mx]); mx++)
							vx = vx * 10 + x [mx] - '0';

						for (; my < ly && char.IsDigit(y[my]); my++)
							vy = vy * 10 + y [my] - '0';

						if (vx != vy)
							return vx > vy ? 1 : -1;
					}

					if (mx < lx && my < ly && x [mx] != y [my])
						return x [mx] > y [my] ? 1 : -1;
				}

				return lx - ly;
			}
		}
	}
}